﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;

namespace CNG
{


    /// <summary>
    ///     Native wrappers for bcrypt CNG APIs.
    ///     
    ///     The general pattern for this interop layer is that the BCryptNative type exports a wrapper method
    ///     for consumers of the interop methods.  This wrapper method puts a managed face on the raw
    ///     P/Invokes, by translating from native structures to managed types and converting from error
    ///     codes to exceptions.
    /// </summary>
    internal static class BCryptNative
    {
        //
        // Enumerations
        //

        /// <summary>
        ///     Flags for BCryptOpenAlgorithmProvider
        /// </summary>
        [Flags]
        internal enum AlgorithmProviderOptions
        {
            None = 0x00000000,
            HmacAlgorithm = 0x00000008,                           // BCRYPT_ALG_HANDLE_HMAC_FLAG
        }

        /// <summary>
        ///     Result codes from BCrypt APIs
        /// </summary>
        internal enum ErrorCode
        {
            Success = 0x00000000,                                       // STATUS_SUCCESS
            AuthenticationTagMismatch = unchecked((int)0xC000A002),     // STATUS_AUTH_TAG_MISMATCH
            BufferToSmall = unchecked((int)0xC0000023),                 // STATUS_BUFFER_TOO_SMALL
        }

        internal static class ProviderName
        {
            internal const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider";      // MS_PRIMITIVE_PROVIDER
        }

        [SecurityCritical(SecurityCriticalScope.Everything)]
        [SuppressUnmanagedCodeSecurity]
        private static class UnsafeNativeMethods
        {
            [DllImport("bcrypt.dll")]
            internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
                                                                         [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId,
                                                                         [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation,
                                                                         AlgorithmProviderOptions dwFlags);

            [DllImport("bcrypt.dll")]
            internal static extern ErrorCode BCryptGenRandom(SafeBCryptAlgorithmHandle hAlgorithm,
                                                             [In, Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbBuffer,
                                                             int cbBuffer,
                                                             int dwFlags);
        }

        /// <summary>
        ///     Fill a buffer with radom bytes
        /// </summary>
        [SecurityCritical]
        [SecurityTreatAsSafe]
        internal static void GenerateRandomBytes(SafeBCryptAlgorithmHandle algorithm, byte[] buffer)
        {
            Debug.Assert(algorithm != null, "algorithm != null");
            Debug.Assert(!algorithm.IsClosed && !algorithm.IsInvalid, "!algorithm.IsClosed && !algorithm.IsInvalid");
            Debug.Assert(buffer != null, "buffer != null");

            ErrorCode error = UnsafeNativeMethods.BCryptGenRandom(algorithm,
                                                                  buffer,
                                                                  buffer.Length,
                                                                  0);
            if (error != ErrorCode.Success)
            {
                throw new CryptographicException(Win32Native.GetNTStatusMessage((int)error));
            }
        }

        /// <summary>
        ///     Open a handle to a BCrypt algorithm provider
        /// </summary>
        [SecurityCritical]
        internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation)
        {
            return OpenAlgorithm(algorithm, implementation, AlgorithmProviderOptions.None);
        }

        [SecurityCritical]
        internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm,
                                                                string implementation,
                                                                AlgorithmProviderOptions options)
        {
            Debug.Assert(!String.IsNullOrEmpty(algorithm), "!String.IsNullOrEmpty(algorithm)");
            Debug.Assert(!String.IsNullOrEmpty(implementation), "!String.IsNullOrEmpty(implementation)");

            SafeBCryptAlgorithmHandle algorithmHandle = null;
            ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
                                                                              algorithm,
                                                                              implementation,
                                                                              options);
            if (error != ErrorCode.Success)
            {
                throw new CryptographicException(Win32Native.GetNTStatusMessage((int)error));
            }

            return algorithmHandle;
        }
    }

}
